package cn.reineko.csmall.passport.ex.handler;

import cn.reineko.csmall.passport.ex.ServiceCode;
import cn.reineko.csmall.passport.ex.ServiceException;
import cn.reineko.csmall.passport.web.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

/**
 * 全局异常处理器
 *
 * @author reineko
 * @version 1.0.0
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 当程序在Controller抛出自定义的ServiceException异常时，统一处理此异常
     */
    @ExceptionHandler
    public JsonResult<Void> handleServiceException(ServiceException e) {
        log.debug("处理ServiceException，serviceCode={}，message={}", e.getServiceCode().getCode(), e.getMessage());
        return JsonResult.fail(e);
    }

    /**
     * 当引用了Validation框架（参数检查）时，参数输入的值不符合注解要求时，处理此异常
     */
    @ExceptionHandler
    public JsonResult<Void> handleBindException(BindException e) {
        log.debug("处理BindException");

        Integer serviceCode = ServiceCode.ERR_BAD_REQUEST.getCode();

        StringBuilder messageBuilder = new StringBuilder();
        List<FieldError> fieldErrors = e.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            messageBuilder.append(fieldError.getDefaultMessage());
        }

        String message = messageBuilder.toString();
        return JsonResult.fail(serviceCode, message);
    }

    /**
     * 当引用了Validation框架（参数检查），处理必填字段没有传入值，或违反唯一键重复插入（本身表中存在相同的数据）
     */
    @ExceptionHandler
    public JsonResult<Void> handleConstraintViolationException(ConstraintViolationException e) {
        log.debug("处理ConstraintViolationException");

        Integer serviceCode = ServiceCode.ERR_BAD_REQUEST.getCode();

        StringBuilder messageBuilder = new StringBuilder();
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            messageBuilder.append(constraintViolation.getMessage());
        }

        String message = messageBuilder.toString();
        return JsonResult.fail(serviceCode, message);
    }

    /**
     * 当程序在Controller抛出的异常不是以上异常时，由Throwable统一处理
     *      如抛出此异常，需分析异常情况，并添加响应的异常处理机制
     */
    @ExceptionHandler
    public JsonResult<Void> handleThrowable(Throwable e) {
        log.debug("处理Throwable");
        e.printStackTrace();

        Integer serviceCode = 99999;
        String message = "程序运行过程中出现未知错误，请联系系统管理员！";
        return JsonResult.fail(serviceCode, message);
    }

}